package com.ht.api.bigtask;

import java.util.Arrays;
import java.util.List;

import com.ht.api.db.Db;
import com.ht.api.db.DbConfig;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.exception.HtException;
import com.ht.api.db.task.HtTaskExecutor;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 大任务管理器
 * @author asq
 * @createTime 2025年1月15日 09:05:26
 */
@Slf4j
public class BigTaskExecutor {
	/**
	 * 构造一个大任务并开始执行
	 * @param appId 应用ID
	 * @param name 任务名称
	 * @param bigTaskList 子任务集合
	 */
	public static void start(String appId, String name, List<BigTask> bigTaskList) {
		start(appId, name, bigTaskList.toArray(new BigTask[0]));
	}
	
	/**
	 * 构造一个大任务并开始执行
	 * @param appId 应用ID
	 * @param name 任务名称
	 * @param bigTasks 多个子任务
	 */
	public static void start(String appId, String name, BigTask... bigTasks) {
		if (bigTasks == null || bigTasks.length == 0) {
			throw new HtException("至少需要一个具体任务实例~");
		}
		
		// 查询是否已经有相同的任务在执行中，防止重复执行任务
		Entity entity = Db.createSqlParser("big_task").where("name", name).where("status", "!=", BigTask.SUCCESS).findOne();
		if (entity != null) {
			throw new HtException("同名任务正在执行中，不可重复执行~");
		}
		
		// 将多个子任务组装成一个队列
		BigTask firstTask = chainTask(bigTasks);
		
		// 创建一个新任务
		Entity entityTask = createBigTaskEntity(appId, name, bigTasks);
		
		// 从第一个任务开始执行
		HtTaskExecutor.execute(() -> {
			firstTask.execute(entityTask);
		});
	}
	
	/**
	 * 恢复执行数据库中未成功的任务
	 */
	public static void continueBigTask() {
		DbList list = Db.createSqlParser("big_task").where("status", "!=", BigTask.SUCCESS).query(DbConfig.useSysDb());
		for (Entity task : list) {
			// 将多个子任务组装成一个队列
			BigTask firstTask = restoreTaskByDb(task);
			
			// 从第一个任务开始执行
			HtTaskExecutor.execute(() -> {
				firstTask.execute(task);
			});
		}
		log.info("[init]系统启动，恢复{}个大任务继续执行。", list.size());
	}
	
	/**
	 * 根据数据库记录，恢复一个任务队列
	 * @param taskEntity 数据库记录
	 * @return 任务队列的首个任务
	 */
	private static BigTask restoreTaskByDb(Entity taskEntity) {
		// 还原任务列表
		List<String> taskNameList = JSONUtil.toList(taskEntity.getStr("task_list"), String.class);
		List<BigTask> taskList = taskNameList.stream().map(n -> {
			return (BigTask)ReflectUtil.newInstance(n);
		}).toList();
		
		// 将多个任务组装成一个队列
		BigTask firstTask = chainTask(taskList.toArray(new BigTask[0]));
		
		// 还原任务队列中每个任务的进度数据
		JSONObject dataJson = JSONUtil.parseObj(taskEntity.getStr("data"));
		restoreData(firstTask, dataJson);
		
		return firstTask;
	}

	/**
	 * 还原任务队列中每个任务的进度数据
	 * @param firstTask 任务队列的首个任务
	 * @param dataJson 任务队列的进度数据json
	 */
	private static void restoreData(BigTask firstTask, JSONObject dataJson) {
		firstTask.setInfo(dataJson.getJSONObject("info"));
		firstTask.setOver(dataJson.getBool("over"));
		firstTask.setTotalTasks(dataJson.getInt("totalTasks"));
		firstTask.setCompletedTasks(dataJson.getInt("completedTasks"));
		
		// 递归下一个任务节点
		if (firstTask.getNextTask() != null) {
			restoreData(firstTask.getNextTask(), dataJson.getJSONObject("nextTask"));
		}
	}

	/**
	 * 将多个子任务串成一串，然后返回首个任务
	 * @param bigTasks
	 * @return
	 */
	private static BigTask chainTask(BigTask... bigTasks) {
		for (int i = 0; i < bigTasks.length - 1; i++) {
			BigTask taskCur = bigTasks[i];
			BigTask taskNext = bigTasks[i + 1];
			taskCur.setNextTask(taskNext);
		}
		
		return bigTasks[0];
	}
	
	/**
	 * 创建大任务的Entity，以便后面写入数据库
	 * @param appId 应用ID
	 * @param name 任务名称
	 * @param bigTasks 多个子任务
	 * @return
	 */
	private static Entity createBigTaskEntity(String appId, String name, BigTask... bigTasks) {
		List<String> taskList = Arrays.stream(bigTasks).map(t -> t.getClass().getName()).toList();
		Entity entityTask = Entity.create("big_task")
				.set("appid", appId)
				.set("name", name)
				.set("status", BigTask.INIT)
				.set("cost_time", 0)
				.set("progress", 0)
				.set("remain_time", "不详")
				.set("task_list", JSONUtil.toJsonStr(taskList))
				.set("msg", "准备中");
		return entityTask;
	}
}
